#!/usr/bin/env python3
# -*- coding:utf-8 -*-

#再次封装框架

import asyncio,os,inspect,logging,functools
from urllib import parse
from aiohttp import web
from apis import APIError

def get(path):
	def decorator(func):
		@functools.wraps(func)
		def wrapper(*args,**kw):
			return func(*args,**kw) 
		wrapper.__method__ = 'GET'
		wrapper.__route__ = path
		return wrapper
	return decorator;

def post(path):
	def decorator(func):
		@functools.wraps(func)
		def wrapper(*args,**kw):
			return func(*args,**kw) 
		wrapper.__method__ = 'POST'
		wrapper.__route__ = path
		return wrapper
	return decorator;

#获取请求的命名关键字参数
def get_required_kw_arg(fn):
	args = []
	params = inspect.signature(fn).parameters
	for name,param in params.items():
		if param.kind == inspect.Parameter.KEYWORD_ONLY and param.default == inspect.Parameter.empty:
			args.append(name)
	return tuple(args)

#获取命名关键字参数
def get_named_kw_args(fn):
	args = []
	params = inspect.signature(fn).parameters
	for name,param in params.items():  
		if param.kind == inspect.Parameter.KEYWORD_ONLY:
			args.append(name)
	return tuple(args)

#判断是否有命名关键字参数
def has_named_kw_arg(fn):
	params = inspect.signature(fn).parameters
	for name,param in params.items():  
		if param.kind == inspect.Parameter.KEYWORD_ONLY: 
			return True
	return False
#是否有关键字参数
def has_var_kw_arg(fn):
	params = inspect.signature(fn).parameters
	for name,param in params.items():  
		if param.kind == inspect.Parameter.VAR_KEYWORD: #自定义关键字参数
			return True
	return False
# inspect.Parameter.VAR_POSITIONAL 自定义参数(也就是可变参数)
#是否有参数名为 request 的参数
def has_request_arg(fn):
	sig = inspect.signature(fn)
	params = sig.parameters;
	found = False
	for name,param in params.items():
		if name == 'request':
			found = True
			continue
		VAR_POSITIONAL = inspect.Parameter.VAR_POSITIONAL #可变参数 *args
		KEYWORD_ONLY = inspect.Parameter.KEYWORD_ONLY #命名关键字参数 (*,name,city)
		VAR_KEYWORD = inspect.Parameter.VAR_KEYWORD #关键字参数 (**kw)
		#如果 request不是命名关键字最后一个参数,则抛出错误
		if found and (param.kind!=VAR_POSITIONAL and param.kind!=KEYWORD_ONLY and param.kind!=VAR_KEYWORD):
			raise ValueError('request parameter must be the last named parameter in function: %s%s' % (fn.__name__, str(sig)))
	return found

class RequestHandler(object):
	"""docstring for RequestHandler"""
	def __init__(self,app,fn):
		self._app = app
		self._func = fn
		self._has_request_arg = has_request_arg(fn) #是否有参数名为 request 的参数
		self._has_var_kw_arg = has_var_kw_arg(fn) #是否有关键字参数
		self._has_named_kw_arg = has_named_kw_arg(fn)#是否有命名关键字参数
		self._naemd_kw_args = get_named_kw_args(fn)
		self._required_kw_arg = get_required_kw_arg(fn)	
		logging.info("RequestHandler __init__ exc func=> %s" % fn.__name__)

	async def __call__(self,request):			
		kw = None
		logging.info("RequestHandler __call__ exc func=> %s,method=>%s " %(self._func.__name__,request.method))			
		if self._has_var_kw_arg or self._has_named_kw_arg or self._has_request_arg:			
			if request.method=='POST':
				if not request.content_type:
					return web.HTTPBadRequest('Missing Content-type')
				ct = request.content_type.lower()
				if ct.startswith('application/json'):
					params = await request.json()
					if not isinstance(params,dict):
						return web.HTTPBadRequest('Json body must be a object')
					kw = params
				elif ct.startswith('application/x-www-form-urlencode') or ct.startswith('multipart/form-data'):
					params = await request.post()
					kw = dict(**params)
				else:
					return web.HTTPBadRequest('Unsupported Content-Type: %s' % request.content_type)
			if request.method == "GET":
				qs = request.query_string
				if qs:					
					kw = dict()
					qsObj = parse.parse_qs(qs,True)
					logging.info("---- qs => %s,qsObj=>%s "%(qs,qsObj))
					for k,v in qsObj.items():
						kw[k] = v[0]
		if kw is None:					
			kw = dict(**request.match_info)
		else:
			if not self._has_var_kw_arg and self._has_named_kw_arg:
				copy = dict()
				for name in self._naemd_kw_args:
					if name in kw:
						copy[name] = kw[name]
				kw = copy
			for k,v in request.match_info.items():
				if k in kw:
					logging.warning('Duplicating arg name in named arg and kw args: %s ' %k)
				kw[k] = v;
		if self._has_request_arg:
			kw['request'] = request
		if self._required_kw_arg:
			for name in self._required_kw_arg:
				if not name in kw:
					return web.HTTPBadRequest('Missing argment:%s' %name )
		logging.info('call with args:%s' %str(kw))		
		try:
			logging.info('self._func name=>%s kw=> %s '%(self._func.__name__,str(kw)))
			r = await self._func(**kw)			
			return r
		except APIError as e:
			return dict(error=e.error,data=e.data,message=e.message)		

def add_static(app):
	path = os.path.join(os.path.dirname(os.path.abspath(__file__)),'static')
	app.router.add_static('/static/',path)
	infStr = 'add static %s => %s => %s ' %('static',path,str(app))
	logging.info(infStr)
def add_route(app,fn):
	method = getattr(fn,'__method__',None)
	path =getattr(fn,'__route__',None)
	if path is None or method is None:
		raise ValueError('@get or @post not defind in %s.' %str(fn)) 
	if not asyncio.iscoroutinefunction(fn) and not inspect.isgeneratorfunction(fn):
		fn = asyncio.coroutine(fn)
	logging.info('add route %s %s => %s(%s)' %(method,path,fn.__name__,','.join(inspect.signature(fn).parameters.keys())))
	app.router.add_route(method,path,RequestHandler(app,fn))

def add_routes(app,module_name):
	n = module_name.rfind('.')
	if n == (-1):
		mod = __import__(module_name,globals(),locals())
	else:
		name = module_name[n+1:]
		mod = getattr(__import__(module_name[:n],globals(),locals(),[name]),name)
	for attr in dir(mod):
		if attr.startswith('_'):
			continue
		fn = getattr(mod,attr)
		if callable(fn):
			method = getattr(fn,'__method__',None)
			path   = getattr(fn,'__route__',None)
			if method and path:
				add_route(app,fn)
